/********************************************************************************
* @file    main.c
* @author  jianqiang.xue
* @Version V1.0.0
* @Date    2021-04-03
* @brief   NULL
********************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>

#include "RTE_Components.h"
#include CMSIS_device_header

#include "bsp_system_clock.h"
#include "bsp_gpio.h"
#include "bsp_uart.h"
#include "bsp_flash.h"
#include "sys_api.h"

#include "check_uid.h"
#include "errorno.h"
#include "str_hex.h"
#include "x_strtok.h"
#include "kv_sys.h"
/* Private Includes ----------------------------------------------------------*/
#define LS_IO
#include "ls_gpio.h"
#include "ls_syscfg.h"
#include "main.h"
#include "dfu.h"
/* Private Define ------------------------------------------------------------*/
#define APP_TARGET_ADDR               (LS_FLASH_APP_ADDR)

#define LOG(...)           \
    do {                   \
        bsp_uart_send_nbyte(BSP_UART_0, NULL, sprintf((char *)bsp_uart_get_txbuff(BSP_UART_0), __VA_ARGS__)); \
    } while (0U)

#define PACK_HEAD_LEN                 2

#define delay_ms                      HAL_Delay
/* Private Typedef -----------------------------------------------------------*/
typedef void (*p_func)(void);

/* Private Macro -------------------------------------------------------------*/
/* Private Variables ---------------------------------------------------------*/
p_func jump_app;
uint32_t jump_addr;

boot_info_t g_boot_info = {
    .boot_state = BOOT_STATE_RUN_APP,            // 0--run app  1--in dfu  2--move ota in app
};
uint8_t uid[16]               = {0};
uint16_t g_uid_sum            = 0;
uint16_t g_flash_uid_sum      = 0; // 通过uid计算一个校验值，作为特征码写入flash，防止程序被读取后，能直接烧录
/* Private Function Prototypes -----------------------------------------------*/
static void chip_lock_check(uint16_t sum) {
    // IC自身计算一个校验值
    get_uid(uid);
    g_uid_sum = compute_checksum2(uid, 16);
    // 判断上位机的校验值于自身校验值是否匹配
    if (g_uid_sum != sum) {
#if LS_OFF_SWD_FUNC
       // 关闭SWD功能 配置PC7和PD1的端子功能模式  周边模块功能模式使能
       RCC->UNLOCK  = 0X55AA6699;
       RCC->SWDIOCR = (0X5A69 << RCC_SWDIOCR_KEY_Pos);
       RCC->UNLOCK  = 0X55AA6698;
#else
       while(1);
#endif
    }
}

/* Boot Mode ---------------------------------------------------------*/
void goto_app(void) {
    if (((*(__IO uint32_t *)APP_TARGET_ADDR) & 0x2FFE0000) == 0x20000000) {
#if LS_APP_JUMP_CRC_CHECK
        // 判断 app 文件累计和是否正确
        uint16_t app_file_sum = compute_checksum2((uint8_t *)LS_FLASH_APP_ADDR, g_boot_info.boot_carry_size);
        if (app_file_sum != g_boot_info.app_file_sum)
            return;
#endif
        /* jump to user application */
        jump_addr = *(__IO uint32_t *)(APP_TARGET_ADDR + 4);
        jump_app = (p_func)jump_addr;
        /* initialize user application's stack pointer */
        __set_PSP(*(__IO uint32_t *)APP_TARGET_ADDR);
        __set_CONTROL(0);
        __set_MSP(*(__IO uint32_t *)APP_TARGET_ADDR);
        jump_app();
    }
}

/**
  * @brief  [跳转App]
  */
void sys_jump_app(void) {
    if (g_boot_info.boot_state != BOOT_STATE_RUN_APP) {
        g_boot_info.boot_state = BOOT_STATE_RUN_APP;
        kv_set_env(LS_KV_KEY_BOOT_INFO,(uint8_t *)&g_boot_info, sizeof(boot_info_t));
        delay_ms(5);
    }
    // 跳转启动
    goto_app();
}

void hardware_init(void) {
    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();
    /* Configure the system clock to HIRC 24MHz*/
    bsp_system_clock_config();
#if LS_BUTTON_ENTRY_BOOT_SUPPORT
    bsp_gpio_set_clk(GPIO_APBx, BOARD_BUTTON_SYS_CLK, true);
    bsp_gpio_init_input(BOARD_BUTTON_SYS_PORT, BOARD_BUTTON_SYS_PIN, BOARD_BUTTON_SYS_PULL);

    bsp_gpio_set_clk(GPIO_APBx, BOARD_BUTTON_USB_CLK, true);
    bsp_gpio_init_input(BOARD_BUTTON_USB_PORT, BOARD_BUTTON_USB_PIN, BOARD_BUTTON_USB_PULL);
#endif
}

/**
 * @brief  从FLASH读取boot信息配置
 */
bool flash_read_boot_info(void) {
    uint8_t *p = kv_get_env(LS_KV_KEY_BOOT_INFO);
    if (p != NULL)
        memcpy((uint8_t *)&g_boot_info, p, sizeof(boot_info_t));
    else
        memset(&g_boot_info, 0, sizeof(boot_info_t));
    return true;
}

/**
 * @brief  从FLASH读取uid信息配置
 */
bool flash_read_uid(void) {
    uint8_t *p = kv_get_env(LS_KV_KEY_UID);
    if (p != NULL)
        memcpy((uint8_t *)&g_flash_uid_sum, p, sizeof(g_flash_uid_sum));
    else
        memset(&g_flash_uid_sum, 0, sizeof(g_flash_uid_sum));
    return true;
}

/*
 * main: initialize and start the system
 */
int main(void) {
    bool dfu_mode = false;
    hardware_init();
//    delay_ms(5);
    kv_gc_check();
    // 读取FLASH
    flash_read_boot_info();
    flash_read_uid();
#if LS_TAMPER_PROOF_SUPPORT
    // 程序防盗检测
    chip_lock_check(g_boot_info.chip_lock);
#endif
#if LS_BUTTON_ENTRY_BOOT_SUPPORT
    // 上电按键按下，则进入boot模式
    if (bsp_gpio_get_state(BOARD_BUTTON_SYS_PORT, BOARD_BUTTON_SYS_PIN) == BOARD_BUTTON_SYS_PRESS_LEVEL &&
        bsp_gpio_get_state(BOARD_BUTTON_USB_PORT, BOARD_BUTTON_USB_PIN) == BOARD_BUTTON_USB_PRESS_LEVEL)
#else
    if (0)
#endif
    {
boot:
        g_boot_info.boot_state = BOOT_STATE_IN_DFU;
        dfu_main();
    } else {
        if (g_boot_info.boot_carry_size == 0xFFFFFFFF)
            g_boot_info.boot_carry_size = 0;
        if (g_boot_info.boot_state == 0xFF)
            g_boot_info.boot_state = BOOT_STATE_RUN_APP;
        if (g_boot_info.boot_state == BOOT_STATE_MOVE_OTA_IN_APP) {
            // 搬运文件大小异常
            if (g_boot_info.boot_carry_size > LS_FLASH_APP_SIZE / 2) {
                dfu_mode = true;
                goto boot;
            }
            bool state = false;
            state = bsp_flash_carry(LS_FLASH_APP_ADDR, LS_FLASH_OTA_ADDR, g_boot_info.boot_carry_size);
            if (state == true)
                sys_jump_app();
        } else if (g_boot_info.boot_state == BOOT_STATE_IN_DFU)
            dfu_mode = true;

        g_boot_info.boot_run_cnt++;

        if (dfu_mode)
            goto boot;

        goto_app();
        goto boot;
    }
    while(1);
}
